package com.luom.fsp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.luom.fsp.common.ErrorCode;
import com.luom.fsp.common.WeixinSessionResponse;
import com.luom.fsp.constant.CommonConstant;
import com.luom.fsp.constant.RedisConstant;
import com.luom.fsp.constant.UserConstant;
import com.luom.fsp.exception.BusinessException;
import com.luom.fsp.exception.ThrowUtils;
import com.luom.fsp.mapper.UserMapper;
import com.luom.fsp.model.dto.user.*;
import com.luom.fsp.model.entity.User;
import com.luom.fsp.model.enums.UserRoleEnum;
import com.luom.fsp.model.vo.CouponVO;
import com.luom.fsp.model.vo.LoginUserVO;
import com.luom.fsp.model.vo.UserVO;
import com.luom.fsp.service.UserService;
import com.luom.fsp.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.luom.fsp.constant.UserConstant.USER_LOGIN_STATE;

/**
 * 用户服务实现
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    /**
     * 盐值，混淆密码
     */
    public static final String SALT = "luom";

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Value("${weixin.appid}")
    private String appId;

    @Value("${weixin.secret}")
    private String appSecret;

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private ObjectMapper objectMapper;

    @Async("taskExecutor") // 指定使用配置的线程池
    public void saveUserToRedisAsync(User user) {
        try {
            // 调用实际的存储方法
            saveUserToRedis(user);
        } catch (Exception e) {
            log.error("保存用户到Redis失败，用户ID: {}", user.getId(), e);
        }
    }

    /**
     * 获取当前登录用户
     *
     * @param request
     * @return
     */
    @Override
    public User getLoginUser(HttpServletRequest request) {
        String token = JwtUtil.getToken(request);
        if (StringUtils.isBlank(token)) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "未登录");
        }
        Map<String, Object> objectMap = JwtUtil.parseToken(token);
        if (objectMap == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "token已失效");
        }
        Long userId = Long.parseLong(objectMap.get("id").toString());

        // 从redis中获取用户信息
        User user;
        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(RedisConstant.LOGIN_USER_KEY + userId))) {
            Map<Object, Object> map = stringRedisTemplate.opsForHash().entries(RedisConstant.LOGIN_USER_KEY + userId);
            user = BeanUtil.fillBeanWithMap(map, new User(), false);
            return user;
        }
        // 从数据库中获取用户信息
        user = this.getById(userId);
        if (user == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "用户不存在");
        }
        // 存储到Redis中
        saveUserToRedisAsync(user);
        return user;
    }


    /**
     * 是否为管理员
     *
     * @param request
     * @return
     */
    @Override
    public boolean isAdmin(HttpServletRequest request) {
        User loginUser = getLoginUser(request);
        return isAdmin(loginUser);
    }

    @Override
    public boolean isAdmin(User user) {
        return user != null && UserRoleEnum.ADMIN.getValue().equals(user.getUserRole());
    }

    /**
     * 用户注销
     *
     * @param request
     */
    @Override
    public boolean userLogout(HttpServletRequest request) {
        if (request.getSession().getAttribute(USER_LOGIN_STATE) == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "未登录");
        }
        // 移除redis
        User user = getLoginUser(request);
        this.deleteUserToRedisAsync(user);
        return true;
    }

    @Override
    public LoginUserVO getLoginUserVO(User user) {
        if (user == null) {
            return null;
        }
        LoginUserVO loginUserVO = new LoginUserVO();
        BeanUtils.copyProperties(user, loginUserVO);
        return loginUserVO;
    }

    @Override
    public UserVO getUserVO(User user) {
        if (user == null) {
            return null;
        }
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(user, userVO);
        return userVO;
    }

    @Override
    public List<UserVO> getUserVO(List<User> userList) {
        if (CollUtil.isEmpty(userList)) {
            return new ArrayList<>();
        }
        return userList.stream().map(this::getUserVO).collect(Collectors.toList());
    }

    @Override
    public QueryWrapper<User> getQueryWrapper(UserQueryRequest userQueryRequest) {

        if (userQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
        }
        Long id = userQueryRequest.getId();
        String mpOpenId = userQueryRequest.getOpenid();
        String userName = userQueryRequest.getUserName();
        String userRole = userQueryRequest.getUserRole();
        String email = userQueryRequest.getEmail();
        String phone = userQueryRequest.getPhone();
        String sortField = userQueryRequest.getSortField();
        String sortOrder = userQueryRequest.getSortOrder();
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(id != null, "id", id);
        queryWrapper.eq(StringUtils.isNotBlank(mpOpenId), "openid", mpOpenId);
        queryWrapper.eq(StringUtils.isNotBlank(userRole), "userRole", userRole);
        queryWrapper.like(StringUtils.isNotBlank(userName), "userName", userName);
        queryWrapper.eq(StringUtils.isNotBlank(email), "email", email);
        queryWrapper.like(StringUtils.isNotBlank(phone), "phone", phone);
        queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                sortField);
        return queryWrapper;
    }

    /*@Override
    public LoginUserVO userLoginByEmail(String userEmail, String code, HttpServletRequest request) {
        // 1. 校验
        if (StringUtils.isAnyBlank(userEmail, code)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
        }
        if (RegexUtils.isEmailInvalid(userEmail)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "邮箱错误");
        }
        // 2. 加密
//        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + code).getBytes());

        //3.从redis中获取验证码并校验
        String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + userEmail);
        if (cacheCode == null || !cacheCode.equals(code)) {
            //4.不一致，返回错误信息
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"验证码错误");
        }

        //5.一致，根据邮箱查询用户
        User user = query().eq("email", userEmail).one();

        //6.不存在，创建新用户并保存
        if (user == null) {
            user = createWithEmail(userEmail);
        }
        // 7. 记录用户的登录态
        request.getSession().setAttribute(USER_LOGIN_STATE, user);
        return this.getLoginUserVO(user);
    }*/

    @Override
    public String adminLogin(AdminLoginRequest adminLoginRequest) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();

        // 根据手机号、邮箱或用户名查询
        queryWrapper.lambda()
                .eq(User::getPhone, adminLoginRequest.getUser())
                .or()
                .eq(User::getEmail, adminLoginRequest.getUser())
                .or()
                .eq(User::getUserName, adminLoginRequest.getUser());

        // 查询用户
        List<User> userList = baseMapper.selectList(queryWrapper);
        if (CollUtil.isEmpty(userList)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在或密码错误");
        }
        User user = userList.get(0);

        // 验证密码是否正确
        if (user != null && user.getUserPassword().equals(DigestUtils.md5DigestAsHex((SALT + adminLoginRequest.getUserPassword()).getBytes()))) {
            // 密码正确，返回用户信息
            if (!user.getUserRole().equals(UserConstant.ADMIN_ROLE)) {
                throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "不是管理员");
            }
            // 异步存储redis
            saveUserToRedisAsync(user);
            return JwtUtil.createToken(user);
        } else {
            // 密码错误或用户不存在，返回相应的错误信息
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在或密码错误");
        }
    }

    @Override
    public Boolean userRegister(UserRegisterRequest userRegisterRequest) {
        String phone = userRegisterRequest.getPhone();
        String userPassword = userRegisterRequest.getUserPassword();
        String checkPassword = userRegisterRequest.getCheckPassword();
        if (StringUtils.isAnyBlank(phone, userPassword, checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
        }
        if (phone.length() > 20) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机号过长");
        }
        if (userPassword.length() < 8 || checkPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户密码过短");
        }
        if (!userPassword.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
        }
        User user = new User();
        // 手机号不能重复
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("phone", phone);
        long count = baseMapper.selectCount(queryWrapper);
        if (count > 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机号重复");
        }
        user.setPhone(phone);
        user.setUserPassword(DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes()));
        user.setUserName("user_" + RandomUtil.randomString(6));
        return save(user);
    }

    @Override
    public WeixinSessionResponse getSessionKey(String code) throws JsonProcessingException {
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appId +
                "&secret=" + appSecret + "&js_code=" + code + "&grant_type=authorization_code";

        ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
        return objectMapper.readValue(response.getBody(), WeixinSessionResponse.class);
    }

    @Override
    public Long addUser(UserAddRequest userAddRequest) {
        User user = new User();
        BeanUtils.copyProperties(userAddRequest, user);
        // 默认密码 12345678
        String defaultPassword = "12345678";
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + defaultPassword).getBytes());
        user.setUserPassword(encryptPassword);
        boolean result = save(user);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        return user.getId();
    }

    @Override
    public boolean checkUpdate(User user) {
        String userName = user.getUserName();
        String userPassword = user.getUserPassword();
        String phone = user.getPhone();
        String email = user.getEmail();
        String userRole = user.getUserRole();
        if (userName != null) {
            ThrowUtils.throwIf(userName.length() > 20, ErrorCode.PARAMS_ERROR, "用户名过长");
            ThrowUtils.throwIf(!userName.isEmpty() && userName.length() < 4, ErrorCode.PARAMS_ERROR, "用户名过短");
            // 不能重复
            User name = getOne(new QueryWrapper<User>().eq("userName", userName));
            ThrowUtils.throwIf(name != null && !name.getId().equals(user.getId()), ErrorCode.PARAMS_ERROR, "用户名已存在");
        }
        if (phone != null) {
            ThrowUtils.throwIf(RegexUtils.isPhoneInvalid(phone), ErrorCode.PARAMS_ERROR, "手机号格式错误");
            User phoneUser = getOne(new QueryWrapper<User>().eq("phone", phone));
            ThrowUtils.throwIf(phoneUser != null && !phoneUser.getId().equals(user.getId()), ErrorCode.PARAMS_ERROR, "手机号已存在");
        }
        if (email != null) {
            ThrowUtils.throwIf(RegexUtils.isEmailInvalid(email), ErrorCode.PARAMS_ERROR, "邮箱格式错误");
            User emailUser = getOne(new QueryWrapper<User>().eq("email", email));
            ThrowUtils.throwIf(emailUser != null && !emailUser.getId().equals(user.getId()), ErrorCode.PARAMS_ERROR, "邮箱已存在");
        }
        if (userPassword != null) {
            ThrowUtils.throwIf(userPassword.length() < 8, ErrorCode.PARAMS_ERROR, "密码过短");
            user.setUserPassword(DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes()));
        }
        if (userRole != null) {
            ThrowUtils.throwIf(!UserRoleEnum.getValues().contains(userRole), ErrorCode.PARAMS_ERROR, "用户角色错误");
        }
        return true;
    }

    // 用户登录
    @Override
    public String userLogin(AdminLoginRequest adminLoginRequest) {
        String userInput = adminLoginRequest.getUser();
        String userPassword = adminLoginRequest.getUserPassword();

        // 1. 根据邮箱或手机号查询用户（不包含密码条件）
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.and(wrapper ->
                wrapper.eq("email", userInput)
                        .or()
                        .eq("phone", userInput)
        );
        List<User> users = baseMapper.selectList(queryWrapper);

        // 2. 处理用户不存在或重复的情况
        if (users.isEmpty()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在");
        } else if (users.size() > 1) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "存在重复用户");
        }
        User user = users.get(0);

        // 3. 在应用层验证密码
        String expectedHash = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
        if (!user.getUserPassword().equals(expectedHash)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码错误");
        }
        saveUserToRedisAsync(user);
        return JwtUtil.createToken(user);
    }

    // 微信登录
    @Override
    public String wxLogin(UserLoginRequest loginRequest) throws JsonProcessingException {
        String code = loginRequest.getCode();

        if (code == null || code.isEmpty()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "code不能为空");
        }
        // 获取微信会话信息
        WeixinSessionResponse sessionData = getSessionKey(code);
        if (sessionData == null || sessionData.getSession_key() == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "获取微信 session 失败");
        }

        String openid = sessionData.getOpenid();

        User user = getOne(new QueryWrapper<User>().eq("openid", openid));
        if (user == null) {
            // 新用户注册
            user = new User();
            user.setOpenid(openid);
            user.setUserName("user_" + RandomUtil.randomString(6));
            save(user);
        }
        saveUserToRedisAsync(user);
        // 生成 JWT token
        return JwtUtil.createToken(user);
    }

    // 保存用户到 Redis
    @Override
    public void saveUserToRedis(User user) {
        Map<String, Object> map = BeanUtil.beanToMap(user, new HashMap<>(),
                CopyOptions.create()
                        .setIgnoreNullValue(true)
                        .setFieldValueEditor((fieldName, fieldValue) -> fieldValue == null ? "" : fieldValue.toString()));
        stringRedisTemplate.opsForHash().putAll(RedisConstant.LOGIN_USER_KEY + user.getId(), map);
        stringRedisTemplate.expire(RedisConstant.LOGIN_USER_KEY + user.getId(), RedisConstant.LOGIN_USER_TTL, TimeUnit.MINUTES);
    }

    // redis中删除用户
    @Override
    @Async("taskExecutor")
    public CompletableFuture<Boolean> deleteUserToRedisAsync(User user) {
        Boolean deleteResult = stringRedisTemplate.delete(RedisConstant.LOGIN_USER_KEY + user.getId());
        boolean result = false;
        if (deleteResult != null) {
            result = deleteResult;
        }
        return CompletableFuture.completedFuture(result);
    }

    // 更新用户信息
    @Override
    public boolean updateUser(UserUpdateRequest userUpdateRequest) {
        User user = new User();
        BeanUtils.copyProperties(userUpdateRequest, user);
        // 参数校验
        boolean isValid = checkUpdate(user);
        if (!isValid) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        user.setAvatarUrl(userUpdateRequest.getUserAvatar());
        boolean result = updateById(user);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        deleteUserToRedisAsync(user);
        return true;
    }

    /*@Override
    public String getPhoneNumber(String encryptedData, String sessionKey, String iv) {
        // 解密获取用户信息
        String decryptedData = AESUtil.decrypt(encryptedData, sessionKey, iv);

        PhoneNumber phoneNumber = new PhoneNumber();
        // 将解密后的 JSON 字符串转化为用户信息对象
        try {
            if (decryptedData != null) {
                phoneNumber = objectMapper.readValue(decryptedData, PhoneNumber.class);
                log.info("解密后的手机号: {}", phoneNumber.getPhoneNumber());
            } else {
                log.error("解密返回的手机号数据为空！");
            }
        } catch (Exception e) {
            log.error("解密手机号信息失败：{}", e.getMessage());
        }
        if (phoneNumber != null) {
            return phoneNumber.getPhoneNumber();
        }
        throw new BusinessException(ErrorCode.SYSTEM_ERROR, "获取手机号失败");
    }*/


    /*private User createWithEmail(String userEmail) {
        User user = new User();
        user.setEmail(userEmail);
        user.setUserName("user_"+ RandomUtil.randomString(6));
        user.setUserAccount(userEmail);
        user.setUserPassword(DigestUtils.md5DigestAsHex((SALT + "12345678").getBytes()));
        save(user);
        return user;
    }*/
}
